<!--
Copyright (C) 2016-2023 Jones Magloire @Joxit

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<tag-list>
  <material-card class="header">
    <div class="material-card-title-action">
      <material-button
        text-color="var(--neutral-text)"
        color="inherit"
        waves-color="var(--hover-background)"
        waves-center="true"
        href="{ router.home() }"
        icon
      >
        <i class="material-icons">arrow_back</i>
      </material-button>
      <h2>
        Tags of { props.image }
        <div class="source-hint">Sourced from { state.registryName + '/' + props.image }</div>
        <div class="item-count">{ state.tags.length } tags</div>
      </h2>
    </div>
  </material-card>

  <div if="{ !state.loadend }" class="spinner-wrapper">
    <material-spinner></material-spinner>
  </div>

  <pagination
    pages="{ getPageLabels(state.page, getNumPages(state.tags, props.tagsPerPage)) }"
    onPageUpdate="{onPageUpdate}"
  ></pagination>

  <tag-table
    if="{ state.loadend }"
    tags="{state.tags}"
    asc="{state.asc}"
    page="{ state.page }"
    show-content-digest="{props.showContentDigest}"
    show-tag-history="{props.showTagHistory}"
    is-image-remove-activated="{props.isImageRemoveActivated}"
    onReverseOrder="{ onReverseOrder }"
    registry-url="{ props.registryUrl }"
    pull-url="{ props.pullUrl }"
    on-notify="{ props.onNotify }"
    filter-results="{ props.filterResults }"
    on-authentication="{ props.onAuthentication }"
    tags-per-page="{ props.tagsPerPage }"
    is-registry-secured="{ props.isRegistrySecured }"
    on-image-deleted="{ () => state.reload() }"
  >
  </tag-table>

  <pagination
    pages="{ getPageLabels(state.page, getNumPages(state.tags, props.tagsPerPage)) }"
    onPageUpdate="{onPageUpdate}"
  ></pagination>

  <script>
    import { Http } from '../../scripts/http';
    import { DockerImage } from '../../scripts/docker-image';
    import { getNumPages, getPageLabels } from '../../scripts/utils';
    import Pagination from './pagination.riot';
    import TagTable from './tag-table.riot';
    import router from '../../scripts/router';
    import { getTagComparator, taglistOrderParser } from '../../scripts/taglist-order';

    export default {
      components: {
        Pagination,
        TagTable,
      },
      onBeforeMount(props) {
        this.state = {
          registryName: props.registryName,
          tags: [],
          loadend: false,
          asc: true,
          page: router.getPageQueryParam() || 1,
        };
        try {
          this.state.taglistOrder = taglistOrderParser(props.taglistOrder);
        } catch (e) {
          props.onNotify(e);
        }
      },
      onMounted(props, state) {
        this.display(props, state);
        window.addEventListener('resize', this.onResize);
        // this may be run before the final document size is available, so schedule
        //  a correction once everything is set up.
        window.requestAnimationFrame(this.onResize);
        this.tagComparator = getTagComparator(props.taglistOrder);
      },
      display(props, state) {
        state.reload = () => setTimeout(() => this.display(props, state), 1000);
        state.tags = [];
        const self = this;
        const oReq = new Http({
          onAuthentication: props.onAuthentication,
          withCredentials: props.isRegistrySecured,
        });
        oReq.addEventListener('load', function () {
          if (this.status === 200) {
            const tags = (JSON.parse(this.responseText).tags || [])
              .map(
                (tag) =>
                  new DockerImage(props.image, tag, {
                    list: true,
                    registryUrl: props.registryUrl,
                    onNotify: props.onNotify,
                    onAuthentication: props.onAuthentication,
                    useControlCacheHeader: props.useControlCacheHeader,
                    isRegistrySecured: props.isRegistrySecured,
                  })
              )
              .sort(self.tagComparator);
            window.requestAnimationFrame(self.onResize);
            self.update({
              page: Math.min(state.page, getNumPages(tags, props.tagsPerPage)),
              tags,
            });
          } else if (this.status === 404) {
            self.props.onNotify('Server not found', true);
          } else {
            self.props.onNotify(this.responseText, true);
          }
        });
        oReq.addEventListener('error', function () {
          self.props.onNotify(this.getErrorMessage(), true);
          state.tags = [];
        });
        oReq.addEventListener('loadend', function () {
          self.update({
            loadend: true,
          });
        });
        oReq.open('GET', props.registryUrl + '/v2/' + props.image + '/tags/list');
        oReq.send();
        state.asc = true;
      },

      onPageUpdate(idx) {
        const labels = getPageLabels(this.state.page, getNumPages(this.state.tags, this.props.tagsPerPage));
        const page = labels[idx].page;
        this.update({
          page: page,
        });
        router.updatePageQueryParam(page);
      },

      onResize() {
        // window.innerWidth is a blocking access, cache its result.
        const innerWidth = window.innerWidth;
        let chars = 0;
        const max = this.state.tags.reduce(function (acc, e) {
          return e.tag.length > acc ? e.tag.length : acc;
        }, 0);
        if (innerWidth >= 1440) {
          chars = 71;
        } else if (innerWidth < 1024) {
          chars = 0;
        } else {
          // SHA256:12345678 + scaled between 1024 and 1440px
          chars = 15 + 56 * ((innerWidth - 1024) / 416);
        }
        if (max > 20) chars -= max - 20;
        chars = Math.floor(chars);
        this.state.tags.map(function (image) {
          image.trigger('content-digest-chars', chars);
        });
      },

      onReverseOrder() {
        if (this.state.asc) {
          this.state.tags.reverse();
          this.state.asc = false;
        } else {
          this.state.tags.sort(this.tagComparator);
          this.state.asc = true;
        }
        this.update();
      },
      getPageLabels,
      getNumPages,
      router,
    };
  </script>
</tag-list>
